package com.dyrnq.seckill.service.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.dyrnq.seckill.config.Constants;
import com.dyrnq.seckill.entity.SeckillOrder;
import com.dyrnq.seckill.mapper.OrderInfoMapper;
import com.dyrnq.seckill.mapper.SeckillGoodsMapper;
import com.dyrnq.seckill.mapper.SeckillOrderMapper;
import com.dyrnq.seckill.mq.MessageVo;
import com.dyrnq.seckill.mq.Producer;
import com.dyrnq.seckill.mq.redis.RedisProducer;
import com.dyrnq.seckill.service.ISeckillSvc;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.lang.Nullable;

import java.util.Collections;

import static com.dyrnq.seckill.mq.Producer.QUEUE_NAME;


@Slf4j
public abstract class AbstractSeckillSvcImpl implements ISeckillSvc {
    protected final SeckillOrderService seckillOrderService;
    protected final SeckillGoodsMapper seckillGoodsMapper;
    protected final SeckillOrderMapper seckillOrderMapper;
    protected final OrderInfoMapper orderInfoMapper;
    protected final RedissonClient redissonClient;
    protected final StringRedisTemplate stringRedisTemplate;
    protected final Producer producer;

    protected AbstractSeckillSvcImpl(SeckillOrderService seckillOrderService, SeckillGoodsMapper seckillGoodsMapper, SeckillOrderMapper seckillOrderMapper, OrderInfoMapper orderInfoMapper, RedissonClient redissonClient, StringRedisTemplate stringRedisTemplate, @Nullable Producer producer) {
        this.seckillOrderService = seckillOrderService;
        this.seckillGoodsMapper = seckillGoodsMapper;
        this.seckillOrderMapper = seckillOrderMapper;
        this.orderInfoMapper = orderInfoMapper;
        this.redissonClient = redissonClient;
        this.stringRedisTemplate = stringRedisTemplate;
        this.producer = producer;
    }

    @Override
    public long doSeckill_1user1order(long goods_id, long user_id) {

        String lockId = "lock:" + user_id;
        RLock rLock = redissonClient.getLock(lockId);

        boolean lock = rLock.tryLock();

        if (lock) {
            try {
                LambdaQueryChainWrapper<SeckillOrder> query = new LambdaQueryChainWrapper<>(seckillOrderMapper);
                long count = query.eq(SeckillOrder::getUserId, user_id).eq(SeckillOrder::getGoodsId, goods_id).count();
                if (count > 0) {
                    throw new RuntimeException("不允许重复下单！");
                }

                return doSeckill(goods_id, user_id);
            } finally {
                if (rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                    rLock.unlock();
                }
            }

        } else {
            throw new RuntimeException("分布式锁生效");
        }
    }

    public long doSeckill_1user1order_redis(long goods_id, long user_id) {
        boolean redisStream = (producer != null && producer instanceof RedisProducer) ? true : false;
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setLocation(new ClassPathResource("seckill.lua"));
        redisScript.setResultType(Long.class);

        Long result = stringRedisTemplate.execute(
                redisScript,
                Collections.emptyList(),
                String.valueOf(goods_id), String.valueOf(user_id), "0", String.valueOf(redisStream), Constants.REDIS_STREAM_ORDERS_KEY
        );
        int r = result.intValue();

        if (r == 1) {
            seckillGoodsMapper.updateStockCountZero(goods_id);
            throw new RuntimeException("库存不足");
        } else if (r == 2) {
            throw new RuntimeException("重复下单");
        }
        //不用调原方法
//        doSeckill_1user1order(goods_id,user_id);

        if (producer != null) {
            MessageVo msg = new MessageVo();
            msg.setUserId(user_id);
            msg.setGoodsId(goods_id);
            producer.send(QUEUE_NAME, msg);
        } else {
            //同步直接写数据库
            seckillOrderService.insert(user_id, user_id);
        }

        return 0;

    }

}
